#ifndef SCENARIO_HPP_INCLUDED
#define SCENARIO_HPP_INCLUDED

#include <boost/intrusive_ptr.hpp>
#include <boost/scoped_ptr.hpp>

#include <map>
#include <vector>

#include "formula.hpp"
#include "formula_callable.hpp"
#include "gamemap.hpp"
#include "player_fwd.hpp"
#include "scenario_result.hpp"
#include "tile_logic.hpp"
#include "unit.hpp"
#include "wml_node_fwd.hpp"

namespace game {

class scenario;
typedef boost::intrusive_ptr<scenario> scenario_ptr;

class scenario : public game_logic::formula_callable {
public:
	static scenario_ptr get(const std::string& id);

	static scenario* current();

	explicit scenario(wml::const_node_ptr node);
	~scenario();
	void play();
	void draw() const;

	const std::string& title() const { return title_; }

	const std::vector<player_ptr>& players() const { return players_; }

	unit_ptr get_unit(const hex::location& loc);
	const_unit_ptr get_unit(const hex::location& loc) const;
	commander_unit_ptr get_commander_with_id(const std::string& id) const;

	typedef std::map<hex::location, unit_ptr> UnitMap;
	const UnitMap& unit_map() const { return units_; }

	const hex::gamemap& map() const { return map_; }

	void move_unit(unit_ptr unit, const hex::location& loc);

	void remove_dead_units();

	struct Condition {
		std::string description;
	};

	const std::vector<Condition>& victory_conditions() const { return victory_conditions_; }
	const std::vector<Condition>& defeat_conditions() const { return defeat_conditions_; }

	void fire(const std::string& event_name, game_logic::formula_callable_ptr args=game_logic::formula_callable_ptr());
	void execute_command(variant command);

	void remove_current_handler();

	bool finished() const { return result_; }
	void end_scenario(const_scenario_result_ptr result);
	const_scenario_result_ptr result() const;

	int base_gold_reward() const { return base_gold_reward_; }
	int base_experience_reward() const { return base_experience_reward_; }

	int bonus_gold_reward() const { return bonus_gold_reward_; }
	int bonus_experience_reward() const { return bonus_experience_reward_; }

	int performance_rating() const;

	bool add_player_commander(player_ptr player, commander_unit_ptr cmd);
	void add_commander(player_ptr player, commander_unit_ptr cmd);

	int round() const { return round_; }
private:
	//makes it so any units without valid locations will be deployed
	//around their commanders.
	void deploy_units();

	variant get_value(const std::string& key) const;
	void set_value(const std::string& key, const variant& value);

	std::string title_;

	hex::gamemap map_;

	UnitMap units_;

	std::vector<player_ptr> players_;
	player_ptr current_player_;

	int round_;

	std::vector<Condition> victory_conditions_, defeat_conditions_;

	std::map<std::string, std::vector<game_logic::formula_ptr> > handlers_;
	game_logic::formula_ptr* current_handler_;

	std::map<std::string, variant> vars_;

	const_scenario_result_ptr result_;

	int base_gold_reward_;
	int base_experience_reward_;

	int bonus_gold_reward_;
	int bonus_experience_reward_;

	hex::location find_vacant_hex(const hex::location& loc) const;
};

}

#endif
